From: Jasper St. Pierre Date: Sun, 13 Oct 2013 17:04:02 +0000 (-0400) Subject: notificationDaemon: Implement the new GTK+ notifications API X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~3177 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=e0b87f1e143447b375e321212a86b05bdb5375ea;p=gnome-shell.git notificationDaemon: Implement the new GTK+ notifications API The new API is designed to support features like persistence and uses the new org.freedesktop.Application specification for activating actions on notifications. While we won't add support for persistence yet, implement the new notification spec with parity of the old one. https://bugzilla.gnome.org/show_bug.cgi?id=710137 --- diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js index ac5b54324..6a55509d3 100644 --- a/js/ui/notificationDaemon.js +++ b/js/ui/notificationDaemon.js @@ -710,10 +710,208 @@ const FdoNotificationDaemonSource = new Lang.Class({ } }); + +const GtkNotificationDaemonNotification = new Lang.Class({ + Name: 'GtkNotificationDaemonNotification', + Extends: MessageTray.Notification, + + _init: function(source, notification) { + this.parent(source); + + let { "title": title, + "body": body, + "icon": gicon, + "urgent": urgent, + "buttons": buttons, + "default-action": defaultAction, + "default-action-target": defaultActionTarget } = notification; + + this.setUrgency(urgent ? MessageTray.Urgency.CRITICAL : MessageTray.Urgency.NORMAL); + + if (buttons) { + buttons.deep_unpack().forEach(Lang.bind(this, function(button) { + this.addAction(button.label, Lang.bind(this, this._onButtonClicked, button)); + })); + } + + this._defaultAction = defaultAction ? defaultAction.unpack() : null; + this._defaultActionTarget = defaultActionTarget ? defaultActionTarget.unpack() : null; + + this.update(title.unpack(), body ? body.unpack() : null, { gicon: gicon }); + }, + + _activateAction: function(namespacedActionId, target) { + if (namespacedActionId) { + if (namespacedActionId.startsWith('app.')) { + let actionId = namespacedActionId.slice('app.'.length); + this.source.activateAction(actionId, target); + } + } else { + this.source.open(); + } + }, + + _onButtonClicked: function(button) { + let { "action": action, "action-target": actionTarget } = button; + this._activateAction(action, actionTarget); + }, + + _onClicked: function() { + this._activateAction(this._defaultAction, this._defaultActionTarget); + this.parent(); + }, +}); + +const FdoApplicationIface = + + + + + + + + +; +const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface); + +function objectPathFromAppId(appId) { + return '/' + appId.replace(/\./g, '/'); +} + +function getPlatformData() { + let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time()); + return { "desktop-startup-id": startupId }; +} + +function InvalidAppError() {} + +const GtkNotificationDaemonAppSource = new Lang.Class({ + Name: 'GtkNotificationDaemonAppSource', + Extends: MessageTray.Source, + + _init: function(appId) { + this._appId = appId; + this._objectPath = objectPathFromAppId(appId); + + this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop'); + if (!this._app) + throw new InvalidAppError(); + + this._notifications = {}; + + this.parent(this._app.get_name()); + }, + + createIcon: function(size) { + return this._app.create_icon_texture(size); + }, + + _createPolicy: function() { + return new MessageTray.NotificationApplicationPolicy(this._appId); + }, + + _createApp: function() { + return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath); + }, + + activateAction: function(actionId, target) { + let app = this._createApp(); + app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData()); + }, + + open: function() { + let app = this._createApp(); + app.ActivateRemote(getPlatformData()); + }, + + addNotification: function(notificationId, notificationParams) { + if (this._notifications[notificationId]) + this._notifications[notificationId].destroy(); + + let notification = new GtkNotificationDaemonNotification(this, notificationParams); + notification.connect('destroy', Lang.bind(this, function() { + delete this._notifications[notificationId]; + })); + this._notifications[notificationId] = notification; + + this.notify(notification); + }, + + removeNotification: function(notificationId) { + if (this._notifications[notificationId]) + this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED); + }, +}); + +const GtkNotificationsIface = + + + + + + + + + +; + +const GtkNotificationDaemon = new Lang.Class({ + Name: 'GtkNotificationDaemon', + + _init: function() { + this._sources = {}; + + this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this); + this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications'); + + Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null); + }, + + _ensureAppSource: function(appId) { + if (this._sources[appId]) + return this._sources[appId]; + + let source = new GtkNotificationDaemonAppSource(appId); + + source.connect('destroy', Lang.bind(this, function() { + delete this._sources[appId]; + })); + Main.messageTray.add(source); + this._sources[appId] = source; + return source; + }, + + AddNotificationAsync: function(params, invocation) { + let [appId, notificationId, notification] = params; + + let source; + try { + source = this._ensureAppSource(appId); + } catch(e if e instanceof InvalidAppError) { + invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId)); + return; + } + + source.addNotification(notificationId, notification); + + invocation.return_value(null); + }, + + RemoveNotificationAsync: function(params, invocation) { + let [appId, notificationId] = params; + let source = this._sources[appId]; + if (source) + source.removeNotification(notificationId); + + invocation.return_value(null); + }, +}); + const NotificationDaemon = new Lang.Class({ Name: 'NotificationDaemon', _init: function() { this._fdoNotificationDaemon = new FdoNotificationDaemon(); + this._gtkNotificationDaemon = new GtkNotificationDaemon(); }, });